"""
Unit Tests for ObservationTypes Module

Tests all observation type structures, constructors, and utility functions.
"""

using Test
using Dates
using ..GSICoreAnalysis.ObservationTypes

@testset "ObservationTypes Tests" begin
    
    @testset "Geographic Location Tests" begin
        # Test basic construction
        loc = GeographicLocation{Float64}(45.0, -120.0, 850.0)
        @test loc.latitude ≈ 45.0
        @test loc.longitude ≈ -120.0
        @test loc.pressure ≈ 850.0
        
        # Test parametric types
        loc32 = GeographicLocation{Float32}(45.0f0, -120.0f0, 850.0f0)
        @test typeof(loc32.latitude) == Float32
        
        # Test boundary conditions
        @test_nowarn GeographicLocation{Float64}(90.0, 180.0, 1013.25)
        @test_nowarn GeographicLocation{Float64}(-90.0, -180.0, 0.01)
    end
    
    @testset "Quality Metrics Tests" begin
        qm = QualityMetrics{Float64}(0.8, 0.9, 0.7, 0.6, 0.85)
        @test qm.overall_quality ≈ 0.8
        @test qm.background_check ≈ 0.9
        @test qm.spatial_consistency ≈ 0.7
        @test qm.temporal_consistency ≈ 0.6
        @test qm.instrument_quality ≈ 0.85
        
        # Test quality score bounds
        @test 0.0 ≤ qm.overall_quality ≤ 1.0
        @test 0.0 ≤ qm.background_check ≤ 1.0
    end
    
    @testset "Surface Observation Tests" begin
        loc = GeographicLocation{Float64}(40.0, -75.0, 1013.25)
        qm = QualityMetrics{Float64}(0.9, 0.95, 0.85, 0.8, 0.9)
        metadata = Dict{String, Any}("station_id" => "KPHL", "wmo_id" => "72408")
        
        obs = SurfaceObservation{Float64}(
            TEMPERATURE,
            loc,
            DateTime(2024, 1, 15, 12, 0, 0),
            285.15,  # 12°C in Kelvin
            0.5,     # ±0.5K error
            qm,
            metadata,
            [0, 0, 0, 0]  # QC flags
        )
        
        @test obs.observation_type == TEMPERATURE
        @test obs.value ≈ 285.15
        @test obs.error ≈ 0.5
        @test obs.location.latitude ≈ 40.0
        @test obs.metadata["station_id"] == "KPHL"
    end
    
    @testset "Radiosonde Observation Tests" begin
        loc = GeographicLocation{Float64}(39.0, -77.0, 850.0)
        qm = QualityMetrics{Float64}(0.85, 0.9, 0.8, 0.75, 0.9)
        
        profile = AtmosphericProfile{Float64}(
            [1000.0, 925.0, 850.0, 700.0, 500.0],  # pressure levels
            [288.0, 285.0, 280.0, 275.0, 265.0],    # temperature
            [0.015, 0.012, 0.008, 0.004, 0.001],    # humidity
            [5.0, 8.0, 12.0, 18.0, 25.0],           # wind speed
            [180.0, 190.0, 200.0, 210.0, 220.0]     # wind direction
        )
        
        obs = RadiosondeObservation{Float64}(
            TEMPERATURE,
            loc,
            DateTime(2024, 1, 15, 12, 0, 0),
            280.0,
            0.3,
            qm,
            Dict{String, Any}("station_id" => "72405", "balloon_type" => "RS41"),
            [0, 0, 0, 0],
            profile,
            850.0  # observation level
        )
        
        @test length(obs.atmospheric_profile.pressure_levels) == 5
        @test obs.observation_level ≈ 850.0
        @test obs.atmospheric_profile.temperature[3] ≈ 280.0  # 850 hPa level
    end
    
    @testset "Radiance Observation Tests" begin
        loc = GeographicLocation{Float64}(25.0, -80.0, 0.0)  # Satellite observation
        qm = QualityMetrics{Float64}(0.7, 0.8, 0.6, 0.9, 0.85)
        
        radiance_data = SatelliteRadianceData{Float64}(
            "AIRS",
            "Aqua", 
            [1, 4, 7, 10, 15],  # channels
            [220.5, 235.2, 250.8, 265.3, 280.1],  # brightness temperatures
            [0.5, 0.3, 0.4, 0.6, 0.8],  # errors per channel
            45.2,  # scan angle
            35.0   # solar zenith angle
        )
        
        obs = RadianceObservation{Float64}(
            RADIANCE,
            loc,
            DateTime(2024, 1, 15, 12, 30, 0),
            250.8,  # Channel 7 brightness temperature
            0.4,
            qm,
            Dict{String, Any}("satellite" => "Aqua", "instrument" => "AIRS"),
            [0, 0, 0, 0],
            7,  # channel number
            radiance_data
        )
        
        @test obs.channel == 7
        @test obs.radiance_data.instrument_name == "AIRS"
        @test length(obs.radiance_data.channels) == 5
        @test obs.radiance_data.brightness_temperatures[3] ≈ 250.8  # Channel 7
    end
    
    @testset "GPS Observation Tests" begin
        loc = GeographicLocation{Float64}(35.0, -105.0, 0.0)
        qm = QualityMetrics{Float64}(0.9, 0.95, 0.85, 0.9, 0.95)
        
        gps_data = GPSData{Float64}(
            "G01",  # satellite ID
            65.2,   # elevation angle
            180.5,  # azimuth angle
            25.4,   # signal-to-noise ratio
            [1000.0, 925.0, 850.0, 700.0, 500.0],  # impact parameter heights
            [1.000345, 1.000298, 1.000245, 1.000180, 1.000095],  # refractivity
            [0.000005, 0.000004, 0.000006, 0.000008, 0.000012]   # refractivity errors
        )
        
        obs = GPSObservation{Float64}(
            GPS_REFRACTIVITY,
            loc,
            DateTime(2024, 1, 15, 12, 45, 0),
            1.000245,  # refractivity at 850 hPa
            0.000006,
            qm,
            Dict{String, Any}("receiver" => "COSMIC-2", "processing_center" => "UCAR"),
            [0, 0, 0, 0],
            gps_data
        )
        
        @test obs.gps_data.satellite_id == "G01"
        @test obs.gps_data.elevation_angle ≈ 65.2
        @test length(obs.gps_data.impact_heights) == 5
        @test obs.value ≈ 1.000245
    end
    
    @testset "Scatterometer Observation Tests" begin
        loc = GeographicLocation{Float64}(30.0, -90.0, 0.0)
        qm = QualityMetrics{Float64}(0.8, 0.85, 0.75, 0.8, 0.9)
        
        scat_data = ScatterometerData{Float64}(
            "ASCAT",
            "MetOp-B",
            [12.5, 15.2, 18.8],  # wind speeds at different looks
            [225.0, 230.0, 235.0],  # wind directions
            [0.8, 0.6, 0.9],  # quality indicators
            [45.0, 50.0, 55.0],  # incidence angles
            [-12.5, -15.8, -18.2]  # backscatter coefficients
        )
        
        obs = ScatterometerObservation{Float64}(
            WIND_SPEED,
            loc,
            DateTime(2024, 1, 15, 13, 0, 0),
            15.2,  # m/s wind speed
            1.2,   # error estimate
            qm,
            Dict{String, Any}("satellite" => "MetOp-B", "swath" => "left"),
            [0, 0, 0, 0],
            scat_data
        )
        
        @test obs.scatterometer_data.instrument_name == "ASCAT"
        @test obs.scatterometer_data.wind_speeds[2] ≈ 15.2
        @test length(obs.scatterometer_data.backscatter_coefficients) == 3
    end
    
    @testset "Observation Type Conversion Tests" begin
        # Test observation type enum
        @test TEMPERATURE isa ObservationType
        @test PRESSURE isa ObservationType  
        @test WIND_SPEED isa ObservationType
        @test HUMIDITY isa ObservationType
        @test RADIANCE isa ObservationType
        
        # Test type hierarchy
        loc = GeographicLocation{Float64}(40.0, -75.0, 1013.25)
        qm = QualityMetrics{Float64}(0.9, 0.95, 0.85, 0.8, 0.9)
        
        surface_obs = SurfaceObservation{Float64}(
            TEMPERATURE, loc, now(), 285.15, 0.5, qm, 
            Dict{String, Any}(), [0, 0, 0, 0]
        )
        
        @test surface_obs isa ConventionalObservation{Float64}
        @test surface_obs isa AbstractObservation{Float64}
    end
    
    @testset "Error Handling Tests" begin
        # Test invalid quality metrics (should be between 0 and 1)
        @test_nowarn QualityMetrics{Float64}(0.0, 0.0, 0.0, 0.0, 0.0)  # Minimum valid
        @test_nowarn QualityMetrics{Float64}(1.0, 1.0, 1.0, 1.0, 1.0)  # Maximum valid
        
        # Test edge case locations
        @test_nowarn GeographicLocation{Float64}(0.0, 0.0, 1013.25)     # Equator, Prime Meridian
        @test_nowarn GeographicLocation{Float64}(90.0, 180.0, 0.01)     # North Pole, Dateline
        @test_nowarn GeographicLocation{Float64}(-90.0, -180.0, 1100.0) # South Pole, Dateline
    end
    
    @testset "Parametric Type Tests" begin
        # Test Float32 precision
        loc32 = GeographicLocation{Float32}(45.0f0, -120.0f0, 850.0f0)
        qm32 = QualityMetrics{Float32}(0.8f0, 0.9f0, 0.7f0, 0.6f0, 0.85f0)
        
        obs32 = SurfaceObservation{Float32}(
            TEMPERATURE, loc32, now(), 285.15f0, 0.5f0, qm32,
            Dict{String, Any}(), [0, 0, 0, 0]
        )
        
        @test typeof(obs32.value) == Float32
        @test typeof(obs32.error) == Float32
        @test typeof(obs32.location.latitude) == Float32
        
        # Test Float64 precision  
        loc64 = GeographicLocation{Float64}(45.0, -120.0, 850.0)
        qm64 = QualityMetrics{Float64}(0.8, 0.9, 0.7, 0.6, 0.85)
        
        obs64 = SurfaceObservation{Float64}(
            TEMPERATURE, loc64, now(), 285.15, 0.5, qm64,
            Dict{String, Any}(), [0, 0, 0, 0]
        )
        
        @test typeof(obs64.value) == Float64
        @test typeof(obs64.error) == Float64
        @test typeof(obs64.location.latitude) == Float64
    end
end